package org.bluedream.core.config.shiro;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.*;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.bluedream.core.config.FilterChainDefinitionMap;
import org.bluedream.core.config.shiro.loginOrganization.LoginOrgManager;
import org.bluedream.core.utils.PasswordHelper;
import org.bluedream.core.utils.YmlRead;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.servlet.Filter;
import java.util.Map;

@Configuration
public class ShiroConfig {
//    private String contextPath = (String) YmlRead.getValue("application.yml" , "server.servlet.context-path");
    /**
     * 自定义realm
     * @return
     */
    @Bean
    public CustomRealm customRealm(){
        CustomRealm realm = new CustomRealm();
        realm.setCredentialsMatcher(hashedCredentialsMatcher());
        return realm;
    }

    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(customRealm());
        securityManager.setSessionManager(sessionManager());
        // 记住我
        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;                           
    }

    /**
     * 自定义 凭证匹配器
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName(PasswordHelper.ALGORITHM_NAME);
        credentialsMatcher.setHashIterations(PasswordHelper.HASH_ITERATIONS);
        return credentialsMatcher;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        // 装载 securityManager! 必要配置
        factoryBean.setSecurityManager(securityManager);
        // login 认证控制器
        factoryBean.setLoginUrl(YmlRead.getValueToString("application.yml", "shiro.loginUrl"));
        // login success 跳转url
        factoryBean.setSuccessUrl(YmlRead.getValueToString("application.yml", "shiro.successUrl"));
        // login no role or no Permission todo: 验证失败的处理 待测试
        factoryBean.setUnauthorizedUrl("/login");

        Map<String , Filter> filters = factoryBean.getFilters();
        filters.put("authc" , this.authenticationFilter());
        filters.put("logout" , this.logoutFilter());

        FilterChainDefinitionMap chains = new FilterChainDefinitionMap();
        chains.setDefaultFilterChainDefinitions(YmlRead.getValueToString("application.yml", "shiro.defaultFilterChainDefinitions"));
        factoryBean.setFilterChainDefinitionMap(chains.getObject());
        return factoryBean;
    }

    private LogoutFilter logoutFilter(){
        CustomLogoutFilter logoutFilter = new CustomLogoutFilter();
        logoutFilter.setLoginOrgManager(loginOrgManager());
        logoutFilter.setRedirectUrl(YmlRead.getValueToString("application.yml", "shiro.loginUrl"));
        return logoutFilter;
    }

    private FormAuthenticationFilter authenticationFilter(){
        CustomFormAuthenticationFilter filter = new CustomFormAuthenticationFilter();
        filter.setUsernameParam("loginCode");
        filter.setOrgCodeParam("orgCode");
        filter.setRememberMeParam("rememberMe");
        filter.setLoginOrgManager(loginOrgManager());
        return filter;
    }

    /**
     * LifecycleBeanPostProcessor
     * 管理shiro一些bean的生命周期 即bean初始化 与销毁
     * @return
     */
    @Bean(name = {"lifecycleBeanPostProcessor"})
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 开启shiro注解 必要bean，依赖于lifecycleBeanPostProcessor
     * 作用: 用来扫描上下文寻找所有的Advistor(通知器), 将符合条件的Advisor应用到切入点的Bean中，需
     * 要在LifecycleBeanPostProcessor创建后才可以创建
     * @return
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
        proxyCreator.setProxyTargetClass(true);
        return proxyCreator;
    }

    /**
     * AuthorizationAttributeSourceAdvisor
     * 作用：加入shiro注解的使用，不加入这个AOP注解不生效(shiro的注解 例如 @RequiresGuest)
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager());
        return advisor;
    }

    /**
     * session 会话管理设置
     * @return
     */
    @Bean
    public SessionManager sessionManager(){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        // session 失效时间
        sessionManager.setGlobalSessionTimeout(1000*60*60*24);
        // 删除无效session
        sessionManager.setDeleteInvalidSessions(true);
/*        // 去掉URL中的JSESSIONID
        sessionManager.setSessionIdUrlRewritingEnabled(true);

        sessionManager.setSessionIdCookie(rememberMeCookie());*/
        return sessionManager;
    }

    /**
     * 记住我 设置
     * @return
     */
    @Bean
    public RememberMeManager rememberMeManager(){
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
        // 定义 rememberMe Cookie
        rememberMeManager.setCookie(rememberMeCookie());
        return rememberMeManager;
    }

    /**
     * 记住我 cookie设置,需要注意，开启记住我，需要user对象能够实现序列化
     * @return
     */
    @Bean
    public SimpleCookie rememberMeCookie(){
        SimpleCookie cookie = new SimpleCookie();
        // 设置cookie保存时长
        cookie.setMaxAge(30*24*60*60);
        // cookie中的名字
        cookie.setName("rememberMe");
        return cookie;
    }

    /**
     * 前端 shiro标签
     * @return
     */
    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }

    /**
     * 注册 shiro 监听
     * @return
     */
    @Bean(name = "sessionListener")
    public SessionListener sessionListener(){
        return new CustomShiroSessionListener();
    }

    /**
     * Session ID 生成器
     * @return
     */
    @Bean
    public SessionIdGenerator sessionIdGenerator(){
        return new JavaUuidSessionIdGenerator();
    }

    /**
     * todo 未完成
     * @return
     */
    public SessionDAO sessionDAO(){
        MemorySessionDAO sessionDAO = new MemorySessionDAO();
        return sessionDAO;
    }

    @Bean
    public LoginOrgManager loginOrgManager(){
        return new LoginOrgManager();
    }
}
